package ngram_reader.validator;

import function.unary.UnaryPredicate;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;
import ngram.exception.NoNGramFilesFoundException;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import list.functions.ListFunctions;
import main.NGramWorker;
import main.errorUpdate.ErrorUpdate;
import main.textUpdate.StatusUpdate;
import ngram.util.IOUtil;
import processorIO.FileIO;
import processorIO.FilePath;
import swingExtended.progressPanel.ProgressPanel;

/**
 *
 * @author Shimu
 * @date 9-Nov-2013
 */
public abstract class Validator extends NGramWorker{
        
     /**
         * Symbol to denote when a file is valid
         */
        private final String VALIDATE_MARK = "\u2713";

        /**
         * Symbol to denote when a file is corrupt.
         */
        private final String CORRUPT_MARK = "\u2717";

        private final File INPUT_DIR;

        private final String CONFIG_FILE;

        private final boolean DELETE_CORRUPTED_FILES;

        private final boolean VALIDATE_ALL_FILES;

        /**
         * A list of files that have been validated so far
         */
        private final Set<String> VALIDATED_FILES = new HashSet<>();

        /**
         * @param INPUT_DIR
         * @param deleteCorrupted true if corrupted files should be deleted,
         * false otherwise.
         */
        public Validator(ProgressPanel panel, File dir, 
                boolean deleteCorrupted, boolean validateAllFiles) {
            super(panel);
            this.INPUT_DIR = dir;
            this.DELETE_CORRUPTED_FILES = deleteCorrupted;
            this.VALIDATE_ALL_FILES = validateAllFiles;
            this.CONFIG_FILE = FilePath.join(dir.getAbsolutePath(), "validate.txt");
        }

        /**
         * Returns true if the given file is valid, false otherwise
         * 
         * @param stream
         * @return
         * @throws IOException 
         */
        protected abstract boolean validate(DataInputStream stream) throws Exception;
        
        @Override
        public final void process(final String fullPath) throws Exception {

            final String fileName = FilePath.getName(fullPath);
                    
            boolean valid;
            try (DataInputStream stream = IOUtil.getDataInputStream(fullPath)) {
                valid = validate(stream);                
            } 

            if (isCancelled()) {
                publish(new StatusUpdate(PANEL, "Did not finish validating: ", fileName));
                
            } else if (valid) {
                VALIDATED_FILES.add(fileName);   
                publish(new StatusUpdate(PANEL, VALIDATE_MARK + " " + fileName));                             
                
            } else {
                if (DELETE_CORRUPTED_FILES) {
                    // Only delete the files after closing the readers
                    if (new File(fullPath).delete()) {
                        publish(new StatusUpdate(PANEL, "Successfully deleted the file"));
                    } else {
                        publish(new ErrorUpdate(PANEL, "Failed to delete the file: " + fileName));
                    }
                }
                publish(new StatusUpdate(PANEL, CORRUPT_MARK + " " + fileName));
            }
        }

        @Override
        protected String getHeader() {
            return "";
        }

        @Override
        protected void start() {
            VALIDATED_FILES.clear();

            if (!VALIDATE_ALL_FILES) {
                // Load the validation config file if it exists
                try (BufferedReader reader = FileIO.getFileReader(CONFIG_FILE)) {
                    PANEL.getStatusTextPane().println("Validation configuration file detected\n"
                            + "Will not check files that were already validated");
                    VALIDATED_FILES.addAll(FileIO.getContent(reader));

                } catch (FileNotFoundException ex) {
                    PANEL.getStatusTextPane().println("Configuration file : " + CONFIG_FILE + " was not found. So will validate all the files");
                } catch (IOException ex) {
                    PANEL.getErrorTextPane().exceptionPrintln(
                            "WARNING: could not load configuration file from: " + CONFIG_FILE, ex);
                }
            }
        }

        @Override
        protected void done() {
            // Write the list of validated files to the config
            try (BufferedWriter writer = FileIO.getFileWriter(CONFIG_FILE, false)) {
                FileIO.write(VALIDATED_FILES, writer, false);

            } catch (IOException ex) {
                PANEL.getErrorTextPane().exceptionPrintln(
                        "WARNING: could not save configuration file to: " + CONFIG_FILE, ex);
            }
            super.done();
        }

        @Override
        protected List<String> initFiles() throws IOException,
                NoNGramFilesFoundException {
            List<String> files = IOUtil.getFiles(INPUT_DIR);
            if (!VALIDATE_ALL_FILES) {
                // Remove all the files that were already validated
                // from the list
                files = ListFunctions.filter(new UnaryPredicate<String>() {

                    @Override
                    public Boolean evaluate(String arg) {        
                        final String name = FilePath.getName(arg);
                        return !VALIDATED_FILES.contains(name);
                    }
                }, files);
            }
            return files;
        }
}
